#include "common.h"
#include "burst.h"
#include "misc.h"
#include <stdlib.h>
#include <time.h>
#include "memory/cacheL1.h"


CacheL1_line cacheL1[CACHEL1_S][CACHEL1_E];

uint32_t cacheL1_read(CacheL1_addr addr, size_t len)
{
    int i = 0;
    for (; i < CACHEL1_E; ++i)
    {
        if (cacheL1[addr.s][i].valid && cacheL1[addr.s][i].tag == addr.t)
        { // hit
            if (addr.b + len > CACHEL1_B)
            { // 跨块读
                uint32_t l = CACHEL1_B - addr.b;
                uint32_t low;
                switch (l)
                {
                case 4:
                    low = unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 4);
                    break;
                case 3:
                    low = unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 3);
                    break;
                case 2:
                    low = unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 2);
                    break;
                case 1:
                    low = unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 1);
                    break;
                default:
                    panic("len error in cacheL1 read\n");
                }
                uint32_t high = cacheL1_read((CacheL1_addr)(addr.val + l), len - l);
                return (high << (l * 8)) | low;
            }
            else
            {
                switch (len)
                {
                case 4:
                    return unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 4);
                    break;
                case 3:
                    return unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 3);
                    break;
                case 2:
                    return unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 2);
                    break;
                case 1:
                    return unalign_rw((void *)cacheL1[addr.s][i].block + addr.b, 1);
                    break;
                default:
                    panic("len error in cacheL1 read\n");
                }
            }
        }
    }
    // miss
    srand((unsigned)time(NULL));
    int line = rand() % CACHEL1_E;
    CacheL1_addr old = addr;

    cacheL1[addr.s][line].tag = addr.t;
    cacheL1[addr.s][line].valid = 1;
    addr.val &= ~((1 << CACHEL1_b) - 1);
    memcpy(cacheL1[addr.s][line].block, hw_mem + addr.val, CACHEL1_B);
    return cacheL1_read(old, len);
}

void cacheL1_write(CacheL1_addr addr, size_t len, uint32_t data){
    if(addr.b + len > CACHEL1_B){
        uint32_t l = CACHEL1_B - addr.b;
        cacheL1_write(addr,l,data & ((1<<(l*8)) -1));
        cacheL1_write((CacheL1_addr)(addr.val+l),len-l,data >> (l*8));
        return;
    }
    int i = 0;
    for (; i < CACHEL1_E; ++i)
    {
        if (cacheL1[addr.s][i].valid && cacheL1[addr.s][i].tag == addr.t)
        { // hit
                switch (len)
                {
                case 4:
                    ((unalign*)((void*)cacheL1[addr.s][i].block +addr.b))->_4 = data;
                    break;
                case 3:
                    ((unalign*)(void*)(cacheL1[addr.s][i].block +addr.b))->_3 = data;
                    break;
                case 2:
                    ((unalign*)(void*)(cacheL1[addr.s][i].block +addr.b))->_2 = data;
                case 1:
                    ((unalign*)(void*)(cacheL1[addr.s][i].block +addr.b))->_1 = data;
                    break;
                default:
                    panic("len error in cacheL1 write\n");
                }
            dram_write(addr.val,len,data);
            return;
        }
        
    }
    dram_write(addr.val,len,data);
    return; 

    
}
